﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using DiscoveryLogic.Analytics.DataObjects;

namespace DiscoveryLogic.Analytics.Diagrams.Physics
{
	public class Particle
	{
		#region events
		/// <summary>
		/// 
		/// </summary>
		/// <param name="particle"></param>
		public delegate void ParticleMovedEventHandler(Particle particle);

		public event ParticleMovedEventHandler Move;
		#endregion

		#region props
		private double _Mass;
		/// <summary></summary>
		public double Mass
		{
			get
			{
				return this._Mass;
			}
			set
			{
                if (value > 0)
                {
                    _Mass = value;
                    OneOverMass = 1.0 / _Mass;
                }
                else
                {
                    throw new ArgumentException("particle mass can not be zero or less", "value");
                }
			}
		}

		private double _OneOverMass;
		/// <summary></summary>
		public double OneOverMass
		{
			get
			{
				return this._OneOverMass;
			}
			private set
			{
				this._OneOverMass = value;
			}
		}

		private Vector _Position;
		/// <summary></summary>
		public Vector Position
		{
			get
			{
				return this._Position;
			}
			set
			{
				this._Position = value;
			}
		}

		private Vector _Velocity;
		/// <summary></summary>
		public Vector Velocity
		{
			get
			{
				return this._Velocity;
			}
			set
			{
				this._Velocity = value;
			}
		}

		private Vector _Force;
		/// <summary></summary>
		public Vector Force
		{
			get
			{
				return this._Force;
			}
			set
			{
				this._Force = value;
			}
		}

		private ParticleState _State;
		/// <summary></summary>
		public ParticleState State
		{
			get
			{
				return this._State;
			}
			set
			{
				this._State = value;
			}
		}

		private Control _Control;
		/// <summary></summary>
		public Control Control
		{
			get
			{
				return this._Control;
			}
			set
			{
				this._Control = value;
			}
		}

		private bool _ConstrainedToConvas;
		/// <summary></summary>
		public bool ConstrainedToConvas
		{
			get
			{
				return this._ConstrainedToConvas;
			}
			set
			{
				this._ConstrainedToConvas = value;
			}
		}

        private EntityBase _Tag;
        /// <summary></summary>
        public EntityBase Tag
        {
            get
            {
                return this._Tag;
            }
            set
            {
                this._Tag = value;
            }
        }
		#endregion

		#region ctor
		public Particle(double mass, Vector position, bool constrainedToCanvas)
		{
			this.Mass = mass;
			this._Position = position;
			this._ConstrainedToConvas = constrainedToCanvas;
			this._Velocity=new Vector();
			this._Force=new Vector();
		    this._Tag = null;
		}

        public Particle(double mass, Vector position, EntityBase entity)
            :this(mass,position,true)
        {
            this._Tag = entity;
        }
		#endregion

		#region public methods
		public void AddForce(Vector newForce)
		{
			this._Force += newForce;
		}

		public void ResetForce()
		{
		    this._Force = new Vector();
		}

		/// <summary>
		/// make sure control centers around the particle
		/// </summary>
		public void SnapControl()
		{
			if(this._Control !=null)
			{
				this._Control.SetValue(Canvas.LeftProperty, (double) this._Position.X - this._Control.ActualWidth/2.0);
				this._Control.SetValue(Canvas.TopProperty, (double)this._Position.Y - this._Control.ActualHeight / 2.0);
				this._Control.Arrange(new Rect(this._Position.ToPoint(), this._Control.DesiredSize));
			}
		}

		public void MoveToPosition(Vector delta)
		{
			if(!double.IsInfinity(this._Mass))
				this._Position += delta;
		}

		public void SetPosition(Vector position, Rect constraintsRectangle)
		{
			if(this._Control !=null)
			{
				position.X = Math.Min(constraintsRectangle.Right, Math.Max(constraintsRectangle.Left, position.X));
				position.Y = Math.Min(constraintsRectangle.Bottom, Math.Max(constraintsRectangle.Top, position.Y));
			}
			this._Position = position;
			this.FireMoveEvent();
		}
		#endregion

		#region move
		private void FireMoveEvent()
		{
			if (this.Move != null)
				this.Move(this);
		}
		#endregion
	}

    //public static class Extension
    //{
    //    public static Vector ToVector(this Point point)
    //    {
    //        return new Vector(point.X, point.Y);
    //    }

    //    public static Point ToPoint(this Vector vector)
    //    {
    //        return new Point(vector.X, vector.Y);
    //    }
    //}
}
